﻿using Microsoft.Extensions.Localization;
using OfficeOpenXml.FormulaParsing.Excel.Functions.Math;
using PHMEE.Constants;
using PHMEE.Entities.BaseInfo.Units;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Linq.Expressions;
using Volo.Abp;
using Volo.Abp.Domain.Entities.Auditing;

namespace PHMEE
{
    /// <summary>
    /// 船舶类
    /// </summary>
    public abstract class Vessel : FullAuditedAggregateRoot<Guid>
    {
        protected Vessel()
        {
            // Required by EF Core
        }

        public Vessel(Guid id, Guid fleetId, string name, string nameen, string imo) : base(id)
        {
            FleetId = fleetId;
            SetName(name);
            SetNameEn(nameen);
            SetImo(imo);
        }


        /// <summary>
        /// 船舶名
        /// </summary>
        public string Name { get; private set; }

        /// <summary>
        /// 英文名
        /// </summary>
        public string NameEn { get; private set; }

        /// <summary>
        /// IMO号
        /// </summary>
        public string IMO { get; private set; }

        /// <summary>
        /// 标签
        /// </summary>
        public string Tags { get; set; }

        /// <summary>
        /// 类型
        /// </summary>
        public VesselEnum Type { get; set; }

        /// <summary>
        /// 呼号
        /// </summary>
        public string CallSign { get; set; }

        /// <summary>
        /// 船旗国
        /// </summary>
        public string FlagState { get; set; }

        /// <summary>
        /// 总吨
        /// </summary>
        public double GrossTon { get; set; }

        /// <summary>
        /// 载重吨
        /// </summary>
        public double DeadWeightTon { get; set; }

        /// <summary>
        /// 净吨重
        /// </summary>
        public double NetTon { get; set; }

        /// <summary>
        /// 现有船舶能效指数(400GT)
        /// </summary>
        public double EEXI { get; set; }

        /// <summary>
        /// 船舶能效设计指数(2013年后)
        /// </summary>
        public double EEDI { get; set; }

        /// <summary>
        /// 船级社
        /// </summary>
        public string ClassificationSociety { get; set; }

        /// <summary>
        /// 流量计模式
        /// </summary>
        public FlowMeterModeEnum? FlowMeterMode { get; set; }

        /// <summary>
        /// 船队Id
        /// </summary>
        public Guid FleetId { get; set; }

        public string Language => CultureInfo.CurrentCulture.TwoLetterISOLanguageName;

        /// <summary>
        /// 多语言展示Name
        /// </summary>
        public string DisplayName => GetLocalizedName();

        public ICollection<Unit> Units { get; set; } = new List<Unit>();

        public Dictionary<string, object> DerivedPropertiesJson { get; set; } = new Dictionary<string, object>();

        public abstract void SerializeProperties(Dictionary<string, object> derivedPropertiesJson);
        public abstract void DeserializeProperties();


        public Vessel SetName(string name)
        {
            Name = Check.NotNullOrEmpty(name, nameof(name), VesselConst.MaxNameLength);
            return this;
        }

        public Vessel SetNameEn(string nameen)
        {
            NameEn = Check.NotNullOrEmpty(nameen, nameof(nameen), VesselConst.MaxNameLength);
            return this;
        }

        public Vessel SetImo(string imo)
        {
            IMO = Check.NotNullOrEmpty(imo, nameof(imo), VesselConst.MaxIMOLength);
            return this;
        }

        public Vessel AddUnit(Guid vesselId, Guid unitId, string name, string nameen, int number, UnitEnum type, Action<Unit> action, IStringLocalizer L)
        {

            var (expression, localizedName) = GetUnitExpression(name, nameen);

            if (Units.Where(expression.Compile()).Any(item => item.Number == number))
            {
                throw new UserFriendlyException(L[PHMEEDomainErrorCodes.UnitNameAlreadyExist], PHMEEDomainErrorCodes.UnitNameAlreadyExist).WithData("Name", localizedName).WithData("Number", number);
            }

            Unit unit = null;

            switch (type)
            {
                case UnitEnum.BallastPumpUnit:
                    unit = new BallastPumpUnit(unitId, vesselId, name, nameen, number);
                    break;
                case UnitEnum.CombustionAirBlowerUnit:
                    unit = new CombustionAirBlowerUnit(unitId, vesselId, name, nameen, number);
                    break;
                case UnitEnum.FuelOilSeparatorUnit:
                    unit = new FuelOilSeparatorUnit(unitId, vesselId, name, nameen, number);
                    break;
                case UnitEnum.LubricatingOilPumpUnit:
                    unit = new LubricatingOilPumpUnit(unitId, vesselId, name, nameen, number);
                    break;
                case UnitEnum.LubricatingOilSepaUnit:
                    unit = new LubricatingOilSepaUnit(unitId, vesselId, name, nameen, number);
                    break;
                case UnitEnum.MainEngineUnit:
                    unit = new MainEngineUnit(unitId, vesselId, name, nameen, number);
                    break;
                case UnitEnum.MainGeneratorUnit:
                    unit = new MainGeneratorUnit(unitId, vesselId, name, nameen, number);
                    break;
                case UnitEnum.SeaWaterPumpUnit:
                    unit = new SeaWaterPumpUnit(unitId, vesselId, name, nameen, number);
                    break;
                case UnitEnum.StartingAirCompressUnit:
                    unit = new StartingAirCompressUnit(unitId, vesselId, name, nameen, number);
                    break;
            }

            action?.Invoke(unit);

            Units.Add(unit);

            return this;
        }

        public Vessel UpdateUnit(Guid unitId, string name, string nameen, Action<Unit> action, IStringLocalizer L)
        {
            var (expression, localizedName) = GetUnitExpression(name, nameen);

            if (Units.Where(expression.Compile()).Any(item => item.Id != unitId))
            {
                throw new UserFriendlyException(L[PHMEEDomainErrorCodes.UnitNameAlreadyExist], PHMEEDomainErrorCodes.UnitNameAlreadyExist).WithData("Name", localizedName);
            }

            var unit = Units.FirstOrDefault(item => item.Id == unitId);

            if (unit == null)
            {
                throw new UserFriendlyException(L[PHMEEDomainErrorCodes.UnitNotFound], PHMEEDomainErrorCodes.UnitNotFound);
            }

            action?.Invoke(unit);

            return this;
        }

        public Vessel RemoveUnit(Guid unitId, IStringLocalizer L)
        {
            var unit = Units.FirstOrDefault(item => item.Id == unitId);

            if (unit == null)
            {
                throw new UserFriendlyException(L[PHMEEDomainErrorCodes.UnitNotFound], PHMEEDomainErrorCodes.UnitNotFound);
            }

            Units.Remove(unit);

            return this;
        }

        public Unit GetUnit(Guid unitId, IStringLocalizer L)
        {
            var unit = Units.FirstOrDefault(item => item.Id == unitId);

            if (unit == null)
            {
                throw new UserFriendlyException(L[PHMEEDomainErrorCodes.UnitNotFound], PHMEEDomainErrorCodes.UnitNotFound);
            }

            return unit;
        }

        public Vessel AddEquipment(Guid unitId, Guid equipmentId, string name, string nameen, int number, EquipmentEnum type, Action<Equipment> action, IStringLocalizer L)
        {
            var unit = GetUnit(unitId, L);

            unit.AddEquipment(equipmentId, name, nameen, number, type, action, L);

            return this;
        }

        public Vessel UpdateEquipment(Guid unitId, Guid equipmentId, string name, string nameen, Action<Equipment> action, IStringLocalizer L)
        {
            var unit = GetUnit(unitId, L);

            unit.UpdateEquipment(equipmentId, name, nameen, action, L);

            return this;
        }

        public Vessel RemoveEquipment(Guid unitId, Guid equipmentId, IStringLocalizer L)
        {
            var unit = GetUnit(unitId, L);

            unit.RemoveEquipment(equipmentId, L);

            return this;
        }

        public Equipment GetEquipment(Guid unitId, Guid equipmentId, IStringLocalizer L)
        {
            var unit = GetUnit(unitId, L);

            var equipment = unit.GetEquipment(equipmentId, L);

            return equipment;
        }

        public Vessel AddComponent(Guid unitId, Guid equipmentId, Guid componentId, string name, string nameen, int number, ComponentEnum type, Action<Component> action, IStringLocalizer L)
        {
            var equipment = GetEquipment(unitId, equipmentId, L);

            equipment.AddComponent(equipmentId, componentId, name, nameen, number, type, action, L);

            return this;
        }

        public Vessel UpdateComponent(Guid unitId, Guid equipmentId, Guid componentId, string name, string nameen, Action<Component> action, IStringLocalizer L)
        {
            var equipment = GetEquipment(unitId, equipmentId, L);

            equipment.UpdateComponent(componentId, name, nameen, action, L);

            return this;
        }

        public Vessel RemoveComponent(Guid unitId, Guid equipmentId, Guid componentId, IStringLocalizer L)
        {
            var equipment = GetEquipment(unitId, equipmentId, L);

            equipment.RemoveComponent(componentId, L);

            return this;

        }

        public Component GetComponent(Guid unitId, Guid equipmentId, Guid componentId, IStringLocalizer L)
        {
            var unit = GetUnit(unitId, L);

            var equipment = unit.GetEquipment(equipmentId, L);

            var component = equipment.GetComponent(componentId, L);

            return component;
        }

        public List<UnitEnum> GetUnitTypeOptions()
        {
            var dict = GetVesselToUnitOptions();

            if (dict.TryGetValue(this.Type, out var unitList))
            {
                return unitList;
            }
            return new List<UnitEnum>(); // 返回空列表，如果没有匹配项

        }

        public static Dictionary<VesselEnum, List<UnitEnum>> GetVesselToUnitOptions()
        {
            var dict = new Dictionary<VesselEnum, List<UnitEnum>>
            {
                { VesselEnum.Bulker, new List<UnitEnum> { UnitEnum.BallastPumpUnit, UnitEnum.CombustionAirBlowerUnit, UnitEnum.FuelOilSeparatorUnit, UnitEnum.LubricatingOilPumpUnit, UnitEnum.LubricatingOilSepaUnit, UnitEnum.MainEngineUnit, UnitEnum.MainGeneratorUnit, UnitEnum.SeaWaterPumpUnit, UnitEnum.StartingAirCompressUnit } },
                { VesselEnum.Container, new List<UnitEnum> { UnitEnum.BallastPumpUnit, UnitEnum.CombustionAirBlowerUnit, UnitEnum.FuelOilSeparatorUnit, UnitEnum.LubricatingOilPumpUnit, UnitEnum.LubricatingOilSepaUnit, UnitEnum.MainEngineUnit, UnitEnum.MainGeneratorUnit, UnitEnum.SeaWaterPumpUnit, UnitEnum.StartingAirCompressUnit } },
                { VesselEnum.Tanker, new List<UnitEnum> { UnitEnum.BallastPumpUnit, UnitEnum.CombustionAirBlowerUnit, UnitEnum.FuelOilSeparatorUnit, UnitEnum.LubricatingOilPumpUnit, UnitEnum.LubricatingOilSepaUnit, UnitEnum.MainEngineUnit, UnitEnum.MainGeneratorUnit, UnitEnum.SeaWaterPumpUnit, UnitEnum.StartingAirCompressUnit } }
            };

            return dict;
        }

        public List<BaseInfoTagEnum> GetVesselTagOptions()
        {
            var list = new List<BaseInfoTagEnum>();

            if (!string.IsNullOrEmpty(Tags))
            {
                var arr = Tags.Split(',', StringSplitOptions.RemoveEmptyEntries);

                foreach (var tag in arr)
                {
                    if (Enum.TryParse(tag, out BaseInfoTagEnum result))
                    {
                        list.Add(result);
                    }
                }
            }

            return list;
        }

        public static List<BaseInfoTagEnum> GetLevelTagOptions()
        {
            var list = new List<BaseInfoTagEnum>
            {
                BaseInfoTagEnum.Longitude,
                BaseInfoTagEnum.Latitude,
                BaseInfoTagEnum.SpeedThroughWater,
                BaseInfoTagEnum.SpeedOverGround,
                BaseInfoTagEnum.CourseOverGround,
                BaseInfoTagEnum.BowDraft,
                BaseInfoTagEnum.SternDraft,
                BaseInfoTagEnum.PortsideDraft,
                BaseInfoTagEnum.StarboardDraft,
                BaseInfoTagEnum.UnderKeel,
            };

            return list;

        }


        public (Expression<Func<Unit, bool>> Expression, string LocalizedName) GetUnitExpression(string name, string nameen)
        {
            Expression<Func<Unit, bool>> expression = item => true;

            string localizedName = string.Empty;

            switch (Language)
            {
                case PhmeeConst.LanguageEn:

                    localizedName = nameen;

                    expression = expression.And(item => item.NameEn == nameen);

                    break;
                default:

                    localizedName = name;

                    expression = expression.And(item => item.Name == name);

                    break;
            }

            return (expression, localizedName);
        }

        private string GetLocalizedName()
        {
            string localizedName = Language switch
            {
                PhmeeConst.LanguageEn => NameEn,
                _ => Name
            };

            return localizedName;
        }

    }
}
